package classification;

import java.util.ArrayList;

import base.Category;


/**
 * Classe permettant de classifier les tweets en quatre catégories, 
 * soit en un temps (classification simple), soit en deux temps (classification hiérarchique), 
 * selon le modèle de Bernoulli ou le modèle multinomial
 */
public class Model {

	/**
	 * Le numéro de la catégorie "positive"
	 */
	public static final int POSITIVE = 0;

	/**
	 * Le numéro de la catégorie "negative"
	 */
	public static final int NEGATIVE = 1;

	/**
	 * Le numéro de la catégorie "neutral"
	 */
	public static final int NEUTRAL = 2;

	/**
	 * Le numéro de la catégorie "irrelevant"
	 */
	public static final int IRRELEVANT = 3;

	/**
	 * Le numéro de la catégorie "relevant"
	 */
	public static final int RELEVANT = 4;

	/**
	 * Le numéro de la catégorie considérée
	 */
	private int typeClass;

	/**
	 * Le type de modèle considéré :
	 * - 0: Bernoulli
	 * - 1: multinomial
	 */
	private int typeModel;

	/**
	 * Le type de classification considéré : 
	 * - false: classification simple, directement en quatre catégories (positive, negative, neutral, irrelevant), 
	 * - true: classification hiérarchique, d'abord en deux catégories (relevant, irrelevant), 
	 *   puis en trois sous-catégories (positive, negative, neutral) si la décision précédente est relevant
	 */
	private boolean isHierarchique;

	/**
	 * Le paramètre de lissage
	 */
	private double lissage;

	
	/**
	 * Constructeur d'un objet Model
	 * 
	 * @param isHierarchique
	 *            le type de classification considéré
	 * @param lissage
	 *            le paramètre de lissage
	 * @param typeModel
	 *            le type de modèle considéré
	 */
	public Model(boolean isHierarchique, double lissage, int typeModel) {
		this.isHierarchique = isHierarchique;
		this.lissage = lissage;
		this.typeModel = typeModel;
	}

	/**
	 * Renvoie le nom de la catégorie considérée
	 * 
	 * @return le nom de la catégorie
	 */
	protected String obtenirTypeClass() {
		switch (typeClass) {
		case POSITIVE:
			return "positive";
		case NEGATIVE:
			return "negative";
		case NEUTRAL:
			return "neutral";
		case IRRELEVANT:
			return "irrelevant";
		case RELEVANT:
			return "relevant";
		}
		return "pas de type de classe défini";
	}

	/**
	 * Classe le message d'un tweet passé en paramètre dans l'une des catégorie
	 * Renvoie le nom de la catégorie obtenue
	 * 
	 * @param message
	 *            le message du tweet
	 * @param base
	 *            objet ModelBase
	 * @return le nom de la catégorie du tweet
	 */
	public String obtenirClasse(String message, ModelBase base) {

		/* Si on considère le modèle de Bernoulli */
		if (this.typeModel == 0) {
			/* Si on considère la classification en quatre catégories */
			if (!isHierarchique) {
				double probaPositive = obtenirProbabiliteBernouilli(base
						.getPositive(), message, base, 1);

				double max = probaPositive;
				typeClass = POSITIVE;

				double probaNegative = obtenirProbabiliteBernouilli(base
						.getNegative(), message, base, 1);
				if (max < probaNegative) {
					max = probaNegative;
					typeClass = NEGATIVE;
				}

				double probaNeutral = obtenirProbabiliteBernouilli(base
						.getNeutral(), message, base, 1);
				if (max < probaNeutral) {
					max = probaNeutral;
					typeClass = NEUTRAL;
				}

				double probaIrrelevant = obtenirProbabiliteBernouilli(base
						.getIrrelevant(), message, base, 1);
				if (max < probaIrrelevant) {
					max = probaIrrelevant;
					typeClass = IRRELEVANT;
				}
			}
			/* Si on considère la classification en deux catégories */
			else {
				double probaRelevant = obtenirProbabiliteBernouilli(base
						.getRelevant(), message, base, 1);

				double max = probaRelevant;
				typeClass = RELEVANT;

				double probaIrrelevant = obtenirProbabiliteBernouilli(base
						.getIrrelevant(), message, base, 1);
				if (max < probaIrrelevant) {
					max = probaIrrelevant;
					typeClass = IRRELEVANT;
				}

				/* Si la décision est "RELEVANT", on classe en trois sous-catégories */
				if (typeClass == RELEVANT) {
					double probaPositive = obtenirProbabiliteBernouilli(base
							.getPositive(), message, base, 1);

					max = probaPositive;
					typeClass = POSITIVE;

					double probaNegative = obtenirProbabiliteBernouilli(base
							.getNegative(), message, base, 1);
					if (max < probaNegative) {
						max = probaNegative;
						typeClass = NEGATIVE;
					}

					double probaNeutral = obtenirProbabiliteBernouilli(base
							.getNeutral(), message, base, 1);
					if (max < probaNeutral) {
						max = probaNeutral;
						typeClass = NEUTRAL;
					}
				}
			}
		}
		/* Si on considère le modèle multinomial */
		else {
			/* Si on considère la classification en quatre catégories */
			if (!isHierarchique) {
				double probaPositive = obtenirProbabiliteMultinomial(base
						.getPositive(), message, base);

				double max = probaPositive;
				typeClass = POSITIVE;

				double probaNegative = obtenirProbabiliteMultinomial(base
						.getNegative(), message, base);
				if (max < probaNegative) {
					max = probaNegative;
					typeClass = NEGATIVE;
				}

				double probaNeutral = obtenirProbabiliteMultinomial(base
						.getNeutral(), message, base);
				if (max < probaNeutral) {
					max = probaNeutral;
					typeClass = NEUTRAL;
				}

				double probaIrrelevant = obtenirProbabiliteMultinomial(base
						.getIrrelevant(), message, base);
				if (max < probaIrrelevant) {
					max = probaIrrelevant;
					typeClass = IRRELEVANT;
				}
			}
			/* Si on considère la classification en deux catégories */
			else {
				double probaRelevant = obtenirProbabiliteMultinomial(base
						.getRelevant(), message, base);

				double max = probaRelevant;
				typeClass = RELEVANT;

				double probaIrrelevant = obtenirProbabiliteMultinomial(base
						.getIrrelevant(), message, base);
				if (max < probaIrrelevant) {
					max = probaIrrelevant;
					typeClass = IRRELEVANT;
				}

				/* Si la décision est "RELEVANT", on classe en trois sous-catégories */
				if (typeClass == RELEVANT) {
					double probaPositive = obtenirProbabiliteBernouilli(base
							.getPositive(), message, base, 2);

					max = probaPositive;
					typeClass = POSITIVE;

					double probaNegative = obtenirProbabiliteBernouilli(base
							.getNegative(), message, base, 2);
					if (max < probaNegative) {
						max = probaNegative;
						typeClass = NEGATIVE;
					}

					double probaNeutral = obtenirProbabiliteBernouilli(base
							.getNeutral(), message, base, 2);
					if (max < probaNeutral) {
						max = probaNeutral;
						typeClass = NEUTRAL;
					}
				}
			}
		}
		return obtenirTypeClass();
	}

	/**
	 * Calcule la probabilité pour une catégorie par le modèle de Bernoulli
	 * 
	 * @param category
	 *            la catégorie
	 * @param message
	 *            le message du tweet
	 * @param base
	 *            objet ModelBase
	 * @return la probabilité de la catégorie
	 */
	private double obtenirProbabiliteBernouilli(Category category, String message, ModelBase base, int niveau) {
		
		// double proba = Math.log(1.0 * category.getNbDoc()
		// / (1.0 * base.getNbMessage()));

		int nbClasse = 0;
		if (!isHierarchique)
			nbClasse = 4;
		else
		{
			if(niveau == 1)
				nbClasse = 2;
			else
				nbClasse = 4;
		}

		double proba = 0.0;
		String[] listMots = message.split(" ");
		ArrayList<String> listMotsList = new ArrayList<String>();

		for (int i = 0; i < listMots.length; i++) {
			if (!listMotsList.contains(listMots[i]))
				listMotsList.add(listMots[i]);
		}

		for (String motTemp : base.getVocabulaire()) {
			int nbTweets = category.getNbDocMot(motTemp);

			if (listMotsList.contains(motTemp))
				proba += Math.log(1.0 * (nbTweets + lissage)
						/ ((category.getNbDoc() + lissage * nbClasse) * 1.0));
			else
				proba += Math.log(1 - 1.0 * (nbTweets + lissage)
						/ ((category.getNbDoc() + lissage * nbClasse) * 1.0));
		}
		return proba;
	}

	/**
	 * Calcule la probabilité pour une catégorie par le modèle multinomial
	 * 
	 * @param category
	 *            la catégorie
	 * @param message
	 *            le message du tweet
	 * @param base
	 *            objet ModelBase
	 * @return la probabilité de la catégorie
	 */
	private double obtenirProbabiliteMultinomial(Category category, String message, ModelBase base) {
		
		// double proba = Math.log(1.0 * category.getNbDoc()
		// / (1.0 * base.getNbMessage()));

		double proba = 0.0;
		String[] listMots = message.split(" ");

		for (String motTemp : listMots) {
			int nbOcc = category.getNbOccMot(motTemp);
			proba += Math.log(1.0
					* (nbOcc + lissage)
					/ ((category.getNbMots() + lissage * base.getVocabulaire().size()) * 1.0));
		}
		return proba;
	}
}
